package com.goo;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @desc: 上海三思 情报板对接，情报板支持两种模式
 * 对接方式：
 * 1、预置播放表，根据预置内容，通过发送指定编号命令即可
 * 2、上传play.lst文件播放，自定义播放内容，然后将该文件通过tcp上传到情报板即可播放
 * 参考资料： https://www.tapd.cn/62774055/documents/file_list/1162774055001000023?left_tree=1#!folderId=1162774055001000027&action=ajax_file_list
 * @author: wangsh
 * @time: 2020/11/13 8:46
 */
public class InformationBoardClient {
    private Log log = LogFactory.getLog(InformationBoardClient.class);
    //文件名称
    private String FILE_NAME = "play.lst";
    //分批次发送文件大小
    private Integer FILE_SIZE = 2048;
    //服务地址 202.11.11.1
//    private String host = "localhost";
    private String host = "58.254.32.50";
    //服务端口 2929
    private Integer port = 2929;

    public InformationBoardClient(String host, Integer port) {
        this.host = host;
        this.port = port;
    }

    /**
     * 文件内容
     *
     * @param content
     * @return
     */
    public String createFileContent(String content) {

        String str = "[list]\n";
        str += "item_no=1\n";
        str += "item0=500, 1, 0,\\fs3232\\C016000\\c255255000000谨慎驾驶  保持车距\n";
        return str;
    }

    /**
     * 获取文件名
     *
     * @param filePath
     * @return
     */
    public String getFileName(String filePath) {
        if (StringUtils.isBlank(filePath)) {
            log.error("文件路径不能为空");
            return null;
        }
        int index = filePath.lastIndexOf(File.separator);
        String fileName = filePath.substring(index + 1);
        log.info("FILE_NAME: " + fileName);

        return fileName;
    }

    /**
     * 读取文件内容
     *
     * @return
     * @throws Exception
     */
    public void sendFile(String filePath) throws Exception {
        if (StringUtils.isBlank(filePath)) {
            log.error("文件路径不能为空");
            return;
        }
        byte[] files = FileUtils.readFileToByteArray(new File(filePath));
        System.out.println("文件大小： " + files.length);
        if (files == null || files.length <= 0) {
            log.info("文件内容不能为空");
            return;
        }

        int fileLen = files.length;
        log.info("发送文件大小： " + fileLen);

        if (fileLen < FILE_SIZE) {
            this.sendTcpCommond(getCommond("10", FILE_NAME, 0, files));
        } else if (fileLen == FILE_SIZE) {
            //如果文件被整除，则最后也必须发送文件内容为 0 字节的一帧,0x00000000
            //发送数据,通过soket发送文件
            this.sendTcpCommond(getCommond("10", FILE_NAME, 0, files));
            byte[] t = {};
            this.sendTcpCommond(getCommond("10", FILE_NAME, FILE_SIZE, t));
        } else {
            //大于2048 分包多次发送
            int count = (int) Math.ceil((fileLen % FILE_SIZE));
            log.info("计算次数： " + count);
            sendFile(files, count);
        }
    }


    /**
     * 发送：
     * "10"             2 字节帧类型
     * 文件名           不定长 ASCII 码字符串
     * 分隔符           1 字节，0x2B，表明文件名的结束
     * 文件指针偏移     4字节十六进制数，先发高字节，后发低字节
     * 一段文件内容     不定长，0-2048 字节
     * 应答：
     * 执行结果         1 字节 ASCII 码
     * 错误信息         不定长 ASCII 码字符串
     */
    public void sendFile(byte[] files, int count) {

        log.info("发送文件开始................");
        //i=0   0               2048            2048            (0+2)*2048
        //i=1 2048          (0+2)*2048          (0+2)*2048      (1+2)*2048
        //i=2 (0+2)*2048    (1+2)*2048          (1+2)*2048      (2+2)*2048
        int start = 0;
        int end = FILE_SIZE;
        //tcp发送
        for (int i = 0; i < count; i++) {
            //分包多次发送
            byte[] bytes = Arrays.copyOfRange(files, start, end);
            //通过soket发送文件
            String content = getCommond("10", FILE_NAME, start, bytes);
            this.sendTcpCommond(content);
            start = end;
            end = (i + 2) * FILE_SIZE;
            if (end >= files.length) {
                end = files.length;
            }
            System.out.println("i: " + i);
            System.out.println("start: " + start);
            System.out.println("end: " + end);
        }
        log.info("发送文件结束................");
    }


    /**
     * 创建发送内容
     *
     * @param type：发送命令类型
     * @param fileName：文件名称
     * @param fileOffset：文件指针偏移
     * @param files：文件内容
     * @return
     * @throws UnsupportedEncodingException
     */
    public String getCommond(String type, String fileName, Integer fileOffset, byte[] files) {
        String content = null;
        try {
            content = type + fileName + 0x2B + Integer.toHexString(fileOffset) + new String(files, "GBK");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        log.info("发送内容： " + content);
        return content;
    }

    /**
     * 发送：
     * "10"             2字节帧类型
     * 文件名           不定长 ASCII 码字符串
     * 分隔符           1字节，0x2B，表明文件名的结束
     * 文件指针偏移     4字节十六进制数，先发高字节，后发低字节
     * 一段文件内容     不定长，0-2048 字节
     * 应答：
     * 执行结果         1 字节 ASCII 码
     * 错误信息         不定长 ASCII 码字符串
     */
    public void sendTcpCommond(String content) {

        if (StringUtils.isBlank(content)) {
            log.error("发送内容不能为空");
        }
        log.info("发送内容： " + content);
        log.info("socket客户端启动.........");
        String info = "";
        //1:创建客户端的套接字
        Socket socket = null;
        try {
            //建立连接
            socket = new Socket("58.254.32.50", 2929);
            log.info("客户端建立连接.............");
//            2:获取输出流
            BufferedWriter bufWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//            3 获取输入流
            BufferedReader bufReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//            3:写数据到服务端
            char[] arr = {0x02, 0x30, 0x30, 0x39, 0x38, 0x30, 0x30, 0x35, 0xDD, 0x7F, 0x03};
            bufWriter.write(arr);
            bufWriter.flush();
//            bufWriter.close();  //切记，缓冲流写数据，需要刷空！！！
//            告诉服务器。客户端这边数据写入完毕
            socket.shutdownOutput();

            //4:读取从服务器响应的数据
            info = bufReader.readLine(); //阻塞式方式
            log.info("服务端响应数据：" + info);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("客户端：读取数据失败：" + e);
        } finally {
            this.close(socket);
        }
        System.out.println("socket客户端关闭.........");
    }

    public void sendTcpCommond2(byte[] params) {

        if (params == null || params.length <= 0) {
            log.error("发送内容不能为空");
        }
        StringBuilder sub = new StringBuilder();
        for (byte code : params) {
            sub.append(code + ",");
        }
        log.info("发送内容： " + sub.toString());
        log.info("socket客户端启动.........");
        String info = "";
        //1:创建客户端的套接字
        Socket socket = null;
        try {
            //建立连接
            socket = new Socket("58.254.32.50", 2929);
            log.info("客户端建立连接.............");
            //2:获取输出流
            OutputStream outputStream = socket.getOutputStream();
            //3 获取输入流
            InputStream inputStream = socket.getInputStream();
            //3:写数据到服务端
//            byte[] bytes = {(byte) 0x02, (byte) 0x30, (byte) 0x30, (byte) 0x39, (byte) 0x38, (byte) 0x30, (byte) 0x30, (byte) 0x35, (byte) 0xDD, (byte) 0x7F, (byte) 0x03};
            outputStream.write(params);
            outputStream.flush();
            //告诉服务器。客户端这边数据写入完毕
            socket.shutdownOutput();

            //4:读取从服务器响应的数据
            List<Integer> resList = new ArrayList<>();
            int len = 0;
            while ((len = inputStream.read()) != -1) {
                resList.add(len);
                System.out.println("服务端响应数据：" + len);
            }
            System.out.println("服务端响应数据resList：" + resList);
            Integer resCode = resList.get(0);

        } catch (Exception e) {
            e.printStackTrace();
            log.error("客户端：读取数据失败：" + e);
        } finally {
            this.close(socket);
        }
        System.out.println("socket客户端关闭.........");
    }

    /**
     * 关闭通道
     *
     * @param socket
     */
    private void close(Socket socket) {
        if (socket != null) {
            try {
                //4:关闭套接字
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
                log.error("关闭通道失败：" + e);
            }
        }
    }

    public static void main(String[] args) throws Exception {

        InformationBoardClient client = new InformationBoardClient("localhost", 2929);
//        String fileName = "D:\\play.lst";
//        client.sendFile(fileName);
        //011 高速公路 /严禁逆行
        byte[] bytes = {(byte) 0x02, (byte) 0x30, (byte) 0x30, (byte) 0x39, (byte) 0x38, (byte) 0x30, (byte) 0x31, (byte) 0x31, (byte) 0XAE, (byte) 0xCA, (byte) 0x03};
        //004	保持车距 /严禁超速
        byte[] bytes2 = {(byte) 0x02, (byte) 0x30, (byte) 0x30, (byte) 0x39, (byte) 0x38, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0XCD, (byte) 0x5E, (byte) 0x03};
        //020	交通拥挤 /减速慢行
        byte[] bytes3 = {(byte) 0x02, (byte) 0x30, (byte) 0x30, (byte) 0x39, (byte) 0x38, (byte) 0x30, (byte) 0x32, (byte) 0x30, (byte) 0xEB, (byte) 0xB8, (byte) 0x03};
        //007	注意安全 /谨慎驾驶
        byte[] bytes4 = {(byte) 0x02, (byte) 0x30, (byte) 0x30, (byte) 0x39, (byte) 0x38, (byte) 0x30, (byte) 0x30, (byte) 0x37, (byte) 0xFD, (byte) 0x3D, (byte) 0x03};

        //放命令 005 保持车距 /谨防追尾
//        Result result = client.sendTcpCommond(fileCont);
//        byte[] params = {(byte) 0x02, (byte) 0x30, (byte) 0x30, (byte) 0x39, (byte) 0x38, (byte) 0x30, (byte) 0x30, (byte) 0x35, (byte) 0xDD, (byte) 0x7F, (byte) 0x03};
        client.sendTcpCommond2(bytes);
    }

//    public static byte[] getSenCmd(char type1, char type2, char code1, char code2, char code3) {
//        //帧头0x02  地址0 帧类型0 数据  校验位 帧尾
////        byte[] bytes4 = {(byte) 0x02, (byte) 0x30, (byte) 0x30, (byte) 0x39, (byte) 0x38, (byte) 0x30, (byte) 0x30, (byte) 0x37, (byte) 0xFD, (byte) 0x3D, (byte) 0x03};
//        byte[] bytes = {(byte) 0x02, (byte) 0x30, (byte) 0x30};
//        //数据（发送类型）
//        bytes[3] = (byte) CodeEnum.getEnum(type1).getHex();
//        bytes[4] = (byte) CodeEnum.getEnum(type2).getHex();
//        //数据(播放编码)
//        bytes[5] = (byte) CodeEnum.getEnum(code1).getHex();
//        bytes[6] = (byte) CodeEnum.getEnum(code2).getHex();
//        bytes[7] = (byte) CodeEnum.getEnum(code3).getHex();
//
//        //crc算法 包括地址、帧类型和未经转义的帧数据
//        int[] buffer = {(byte) 0x30, (byte) 0x30, bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]};
//        int crc = CRC.gen_crs(buffer);
//        System.out.println("crc算法: " + crc);
//        String hexStr = RadixUtils.decimalToHexString(crc);
//        System.out.println("转为16进制：" + hexStr);
//        //校验位
////        bytes[8] = (byte)0x03;
////        bytes[9] = (byte)0x03;
//        //帧尾
////        bytes[10] = (byte) 0x03;
//
//        return bytes;
//    }

}
